#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#include "vvsfs.h"

char* device_name;
int device, block_size;
struct vvsfs_block *block=NULL;
struct vvsfs_device_block *device_block=NULL;

static void die(char *mess) {
    fprintf(stderr,"Exit : %s\n",mess);
    exit(1);
}

static void usage(void) {
    die("Usage : journal_test <device name>");
}

static inline void readblock(off_t pos) {
    if (pos != lseek(device,pos,SEEK_SET)){
        free(block);
        free(device_block);
        die("seek set failed");
    }
    if (block_size != read(device,block,block_size)){
        free(block);
        free(device_block);
        die("inode read failed");
    }
}

static inline void write_block(off_t pos) {
    if (pos != lseek(device, pos, SEEK_SET)) {
        // move the file pointer to the correct block
        free(block);
        die("seek set failed");
    }
    if (block_size != write(device, block, block_size)){
        // write the block
        free(block);
        die("inode write failed");
    }
}

int main(int argc, char ** argv) {
    if (argc != 2) usage();

    // open the device for reading
    device_name = argv[1];
    device = open(device_name, O_RDWR);

    //Generate the device info.
    int device_block_size=sizeof(struct vvsfs_device_block);
    device_block=(struct vvsfs_device_block *)malloc(device_block_size);
    //Read the device information.
    if(device_block_size != read(device, device_block, device_block_size)) {
        free(device_block);
        die("Read device info error.");
    }
    //Read the device info.
    block_size=device_block->block_size;
    block=(struct vvsfs_block *)malloc(block_size);
    if(block==NULL) {
        free(device_block);
        die("No enough memory.");
    }
    // print header images.
    printf(HEADER_IMAGES, "Journal Testing Tool", VER_MAJOR, VER_MINOR, VER_PATCH, VER_SPECIAL);
    printf("Device information\n"
           "Block size           : %d bytes\n"
           "Total blocks         : %d blocks\n"
           "Inode blocks         : %d blocks\n"
           "Last used block      : 0x%08x\n",
           device_block->block_size,
           device_block->block_count,
           device_block->inode_block_count,
           device_block->last_used_block);
    //Calculate the root block start position.
    int i, j, block_count=device_block->block_count,
            root_block_index=device_block->inode_block_count+BLOCK_INODE_LIST;
    printf("Root Dir Block Index : 0x%08x\n"
           "Data blocks          : %d blocks\n",
           root_block_index,
           block_count-root_block_index+1);
    int inode_max_count=block_size/sizeof(struct vvsfs_inode),
            total_inodes=inode_max_count*(device_block->inode_block_count);
    printf("Max inode per block  : %d inodes\n"
           "Total inodes         : %d inodes\n\n",
           inode_max_count, total_inodes);
    //Read journal data.
    printf("Writing fake journal state...\n");
    readblock(block_size*BLOCK_JOURNAL_STATE);
    //Setting journal information.
    struct vvsfs_journal_state *journal_state=
            (struct vvsfs_journal_state *)block;
    printf("Journal state        : Writing real data\n");
    journal_state->state = JOURNAL_WRITING;
    printf("Journaled operation  : Writing file\n");
    journal_state->file_operation = JOURNAL_OP_WRITEFILE;
    printf("Journaled inode no   : 1\n");
    journal_state->inode_number = 1;
    printf("Journaled inode data :\n");
    journal_state->inode.block=device_block->inode_block_count +
            BLOCK_INODE_LIST+1;
    printf("  inode block no     : %x\n", journal_state->inode.block);
    printf("  inode ref count    : 1\n");
    journal_state->inode.count=1;
    printf("  inode file size    : 11\n");
    journal_state->inode.size=11;
    printf("  inode compress size: -1\n");
    journal_state->inode.c_size=-1;
    printf("  inode flag         : 0\n");
    journal_state->inode.flag=0;
    printf("  inode uid          : comp3300\n");
    journal_state->inode.uid=0x3e8;
    printf("  inode gid          : comp3300\n");
    journal_state->inode.gid=0x3e8;
    printf("  inode mode         : Regular file\n");
    journal_state->inode.mode=0x81b6;
    write_block(block_size*BLOCK_JOURNAL_STATE);
    printf("\n");
    printf("Writing fake journal data...\n");
    readblock(block_size*BLOCK_JOURNAL_DATA);
    printf("  block ref count    : 1\n");
    block->count=1;
    printf("  block flag         : 0\n");
    block->flag=0;
    printf("  block enc size     : -1\n");
    block->e_size=-1;
    printf("  block hash data    : 0x05961ffa\n");
    block->hash=0x05961ffa;
    printf("  block size         : 11\n");
    block->size=11;
    printf("  block data         : Love Live \\n\n");
    strncpy(block->data, "Love Live!\n", 11);
    write_block(block_size*BLOCK_JOURNAL_DATA);
    printf("\n");
    return 0;
}

